package com.wgo.bpot.client.session;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import com.wgo.bpot.common.transport.exception.RematoException;
import com.wgo.bpot.remote.RemotableDomain;

public class RemoteConceptFactory {
	
	/**
	 * The super-interface of all concepts handled by the proxy. Used for 
	 * validation checking.
	 */
	public static Class<? extends Object> PROXY_SUPER_INTERFACE = null;

	public static Object create(Object actualConcept, LightSession session){
		if ((null == actualConcept) || !PROXY_SUPER_INTERFACE.isInstance(actualConcept)) {
			throw new RematoException("Invalid concept passed. It must implement Persistent and not be null!");
		}
		Object result = null;
		try {
			Class remoteDomainInterface = actualConcept.getClass().getMethod("serverCall").getReturnType();
			if (!remoteDomainInterface.isInterface()) {
				throw new RematoException(actualConcept.getClass().getName() + ".serverCall() method must return an interface type, not a class!");
			}
			RemoteConceptInvocationHandler handler = new RemoteConceptInvocationHandler(actualConcept, session);
			result = Proxy.newProxyInstance(
					actualConcept.getClass().getClassLoader(), new Class[]{remoteDomainInterface},handler
					);
		} catch (Exception ignore) {
//			This concept does not have the serverCall method defined, and hence it is not available for remote operations. This is not considered an exceptional condition. We will return null.
			result = null;
		}
		return result;
	}
	
	
	private static class RemoteConceptInvocationHandler implements InvocationHandler {
		private Object actualConcept;
		private LightSession session;

		public RemoteConceptInvocationHandler(Object actualConcept, LightSession session) {
			this.actualConcept = actualConcept;
			this.session = session;
		}

		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
			Object[] convertedArgs = DomainProxyInvocationHandler.convertArgsToActual(args);
			return session.getDomainServerService().executeRemote((RemotableDomain)actualConcept, method.getName(), convertedArgs);
		}
		
	}
}
